package com.r2coding.article.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.r2coding.article.annotation.SysLog;
import com.r2coding.article.dao.ArticleLabelDao;
import com.r2coding.article.dao.CategoryDao;
import com.r2coding.article.dao.LabelDao;
import com.r2coding.article.entity.ArticleLabelEntity;
import com.r2coding.article.entity.CategoryEntity;
import com.r2coding.article.entity.LabelEntity;
import com.r2coding.article.service.ArticleLabelService;
import com.r2coding.article.vo.req.ArticleReqVo;
import com.r2coding.article.vo.req.QueryArticleReqVo;
import com.r2coding.article.vo.req.UpdateArticleReqVo;
import com.r2coding.article.vo.resp.*;
import com.r2coding.common.constant.ArticleConstant;
import com.r2coding.common.constant.Constant;
import com.r2coding.common.enums.Module;
import com.r2coding.common.enums.OperationType;
import com.r2coding.common.exception.BizCode;
import com.r2coding.common.exception.ServiceException;
import com.r2coding.common.util.PageUtils;
import com.r2coding.common.util.Query;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.r2coding.article.dao.ArticleDao;
import com.r2coding.article.entity.ArticleEntity;
import com.r2coding.article.service.ArticleService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 
 *
 * @author 98k灬
 * @email hk666zx@163.com
 * @date 2022-04-12 23:41:38
 */
@Service("articleService")
public class ArticleServiceImpl extends ServiceImpl<ArticleDao, ArticleEntity> implements ArticleService {
    private static final String VISIT_KEY = "visit_num";

    @Autowired
    private ArticleLabelService articleLabelService;

    @Autowired
    private ArticleLabelDao articleLabelDao;

    @Autowired
    private ArticleDao articleDao;

    @Autowired
    private LabelDao labelDao;

    @Autowired
    private CategoryDao categoryDao;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<ArticleEntity> page = this.page(
                new Query<ArticleEntity>().getPage(params),
                new QueryWrapper<ArticleEntity>()
        );
        return new PageUtils(page);
    }

    @SysLog(module = Module.ARTICLE, operationType = OperationType.INSERT, description = "添加文章")
    @Transactional
    @Override
    public void saveArticle(ArticleReqVo vo) {
        // 新增文章表
        ArticleEntity article = new ArticleEntity();
        BeanUtils.copyProperties(vo, article);
        article.setDeleted(0);
        article.setThumhup(0);
        article.setStatus(vo.getStatus());
        article.setViews(0);
        article.setUpdateTime(new Date());
        article.setCreateTime(new Date());
        String replaceStr = vo.getHtmlContent().replaceAll("<code>", "").replaceAll("</code>", "");
        article.setHtmlContent(replaceStr);
        // 根据标签id查询分类id
        Integer id = vo.getLabelIds().get(0);
        LabelEntity labelEntity = labelDao.selectById(id);
        article.setCategoryId(labelEntity.getCategoryId());
        // 获取用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
        Map<String, Object> map = (Map<String, Object>)details.getDecodedDetails();
        Map<String, Object> userInfo = (Map<String, Object>)map.get("userInfo");
        Integer userId = (Integer)userInfo.get("userId");
        article.setCreaterId(userId);
        this.baseMapper.insert(article);
        // 新增文章标签关系表
        for (Integer labelId : vo.getLabelIds()) {
            ArticleLabelEntity entity = new ArticleLabelEntity();
            entity.setCreateDate(new Date());
            entity.setArticleId(article.getArticleId());
            entity.setLabelId(labelId);
            articleLabelService.save(entity);
        }
    }

    @Override
    public ArticleListRespVo queryClientList(QueryArticleReqVo vo) {
        List<QueryArticleRespVo> listCount = articleDao.queryList(vo.getTitle(), vo.getLabelId(), vo.getCategoryId(), vo.getIsPublic());
        PageHelper.startPage(vo.getCurrent(), vo.getSize());
        List<QueryArticleRespVo> list = articleDao.queryList(vo.getTitle(), vo.getLabelId(), vo.getCategoryId(), vo.getIsPublic());
        if (!CollectionUtils.isEmpty(list)) {
            for (QueryArticleRespVo respVo : list) {
                List<ArticleLabelEntity> articleLabelEntityList
                        = articleLabelDao.selectList(new QueryWrapper<ArticleLabelEntity>().eq("article_id", respVo.getArticleId()));
                List<LabelEntity> collect = articleLabelEntityList.stream().map(item -> {
                    return labelDao.selectById(item.getLabelId());
                }).collect(Collectors.toList());
                respVo.setLabelList(collect);
            }
        }
        ArticleListRespVo articleListRespVo = new ArticleListRespVo();
        articleListRespVo.setSize(listCount.size());
        articleListRespVo.setRespVos(list);
        return articleListRespVo;
    }

    @Override
    public PageInfo<QueryArticleRespVo> queryList(QueryArticleReqVo vo) {
        PageHelper.startPage(vo.getCurrent(), vo.getSize());
        List<QueryArticleRespVo> list = articleDao.queryList(vo.getTitle(), vo.getLabelId(), vo.getCategoryId(), 0);
        List<Integer> articleIds = new ArrayList<>();
        List<QueryArticleRespVo> respVos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            for (QueryArticleRespVo respVo : list) {
                if (!articleIds.contains(respVo.getArticleId())) {
                    respVos.add(respVo);
                    articleIds.add(respVo.getArticleId());
                }
            }
        }
        return new PageInfo<QueryArticleRespVo>(respVos);
    }

    @Override
    public void release(Integer id) {
        ArticleEntity article = new ArticleEntity();
        article.setArticleId(id).setStatus(ArticleConstant.ARTICLE_RELEASE).setUpdateTime(new Date());
        this.baseMapper.updateById(article);
    }

    //@Cacheable(value = "article", key = " 'article_' + #id")
    @Override
    public QueryArticleRespVo queryById(Integer id) {
        List<QueryArticleRespVo> respVos = articleDao.queryById(id);
        QueryArticleRespVo respVo = respVos.get(0);
        // 判断是否存在上一篇文章
        if (respVo.getArticleId() > 1) {
            ArticleEntity article = this.baseMapper.selectById(respVo.getArticleId() - 1);
            if (article != null) {
                respVo.setFrontArticleId(article.getArticleId());
                respVo.setFrontArticleImg(article.getImg());
                respVo.setFrontArticleTitle(article.getTitle());
                respVo.setIsFront(1);
            }
        }
        // 判断是否存在下一篇文章
        ArticleEntity article = this.baseMapper.selectById(respVo.getArticleId() + 1);
        if (article != null) {
            respVo.setIsNext(1);
            respVo.setNextArticleId(article.getArticleId());
            respVo.setNextArticleTitle(article.getTitle());
            respVo.setNextArticleImg(article.getImg());
        }
        return respVo;
    }

    //@Cacheable(value = {"base"}, key = " 'base_' + #root.methodName")
    @Override
    public BaseVo selectBase() {
        System.out.println("查询数据库");
        BaseVo baseVo = new BaseVo();
        QueryWrapper<ArticleEntity> articleWrapper = new QueryWrapper<>();
        articleWrapper.eq("is_public", 1).eq("status", ArticleConstant.ARTICLE_RELEASE);
        Integer articleCount = this.baseMapper.selectCount(articleWrapper);
        baseVo.setArticleCount(articleCount);

        Integer categoryCount = categoryDao.selectCount(new QueryWrapper<CategoryEntity>());
        baseVo.setCategoryCount(categoryCount);

        Integer labelCount = labelDao.selectCount(new QueryWrapper<LabelEntity>());
        baseVo.setLabelCount(labelCount);
        return baseVo;
    }

    //@Cacheable(value = "CategoryAndLabel", key = " 'article_' + #id")
    @Override
    public CategoryAndLabelsRespVo getCategoryAndLabelByArticleId(Integer id) {
        CategoryAndLabelsRespVo respVo = new CategoryAndLabelsRespVo();
        QueryWrapper<ArticleLabelEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("article_id", id);
        List<ArticleLabelEntity> list = articleLabelDao.selectList(wrapper);
        List<Integer> labels = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach( item -> {
                System.out.println("标签id=" + item.getLabelId());
                LabelEntity labelEntity = labelDao.selectById(item.getLabelId());
                labels.add(labelEntity.getLabelId());
            });
        }
        respVo.setLabels(labels);
        LabelEntity labelEntity = labelDao.selectById(list.get(0).getLabelId());
        respVo.setCategoryId(labelEntity.getCategoryId());
        respVo.setCategoryName(labelEntity.getName());
        return respVo;
    }

    @SysLog(module = Module.ARTICLE, operationType = OperationType.DELETE, description = "删除文章")
    // @CacheEvict(value = {"base"}, key = " 'selectBase' ") // 删除缓存
    @Transactional
    @Override
    public void deleteArticle(Integer id) {
        // 删除文章
        this.baseMapper.deleteById(id);
        // 删除标签
        articleLabelDao.delete(new QueryWrapper<ArticleLabelEntity>().eq("article_id", id));
    }

    @SysLog(module = Module.ARTICLE, operationType = OperationType.UPDATE, description = "更新文章")
    @Transactional
    @Override
    public void updateArticle(UpdateArticleReqVo vo) {
        // 根据标签查询分类
        Integer id = vo.getLabels().get(0);
        LabelEntity labelEntity = labelDao.selectById(id);
        // 更新文章信息
        ArticleEntity article = new ArticleEntity();
        BeanUtils.copyProperties(vo, article);
        article.setUpdateTime(new Date());
        article.setCategoryId(labelEntity.getCategoryId());
        this.baseMapper.updateById(article);
        // 更新文章标签信息  先将该文章的所有标签全部删除  然后新增新的标签
        articleLabelDao.delete(new QueryWrapper<ArticleLabelEntity>().eq("article_id", vo.getArticleId()));
        // 只有设置公开时 才需要添加标签与文章的关系信息
        if (vo.getIsPublic() == 1) {
            if (vo.getLabels() != null && vo.getLabels().size() > 0) {
                for (Integer labelId : vo.getLabels()) {
                    ArticleLabelEntity articleLabel = new ArticleLabelEntity();
                    articleLabel.setCreateDate(new Date());
                    articleLabel.setArticleId(vo.getArticleId());
                    articleLabel.setLabelId(labelId);
                    articleLabelDao.insert(articleLabel);
                }
            }
        }
    }

    @Override
    public void thumhup(Integer articleId, Integer count) {
        // 根据文章id查询该文章信息
        synchronized (this) {
            // 点赞或取消点赞的频率限制  15秒
            String key = Constant.ARTICLE_THUMHUP_PREFIX + articleId;
            if (stringRedisTemplate.hasKey(key)) {
                throw new ServiceException(BizCode.THUMHUP_ERROR);
            }
            ArticleEntity article = this.baseMapper.selectById(articleId);
            if (article != null) {
                int thumhup = article.getThumhup() + count;
                article.setThumhup(thumhup);
                this.baseMapper.updateById(article);
            }
            // 记录redis
            stringRedisTemplate.opsForValue().set(key, String.valueOf(articleId), 15, TimeUnit.SECONDS);
        }
    }

    @Cacheable(value = "articleEntity", key = " 'RECOMMEND_ARTICLE' ")
    @Override
    public List<ArticleEntity> getRecommend() {
        PageHelper.startPage(1, 6);
        QueryWrapper<ArticleEntity> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("views");
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public List<VisitNumRespVo> getVisitNum() {
        List<VisitNumRespVo> collect = new ArrayList<>();
        if (stringRedisTemplate.hasKey(VISIT_KEY)) {
            String value = stringRedisTemplate.opsForValue().get(VISIT_KEY);
            JSONArray array = JSONArray.parseArray(value);
            collect = array.stream().map(item -> {
                JSONObject o = (JSONObject) item;
                String dateObj = (String) o.get("date");
                Integer visitNum = (Integer) o.get("visitNum");
                VisitNumRespVo respVo = new VisitNumRespVo(visitNum, dateObj, 0);
                return respVo;
            }).collect(Collectors.toList());
        }
        return collect;
    }

    @Override
    public List<ArticleEntity> newArticle(Integer id) {
        QueryWrapper<ArticleEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("is_public", 1);
        wrapper.orderByDesc("create_time");
        List<ArticleEntity> list = this.baseMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(list)) {
            list.removeIf(articleEntity -> articleEntity.getArticleId().equals(id));
        }
        if (list.size() <= 6) {
            return list;
        } else {
            list.subList(0, 6);
        }
        return list;
    }

    @Override
    public PageInfo<ArticleEntity> getArchives(QueryArticleReqVo req) {
        PageHelper.startPage(req.getCurrent(), req.getSize());
        QueryWrapper<ArticleEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("is_public", 1);
        wrapper.orderByDesc("create_time");
        List<ArticleEntity> articleList = this.baseMapper.selectList(wrapper);
        return new PageInfo<ArticleEntity>(articleList);
    }
}